﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using blackhouse.Payments.Data;
using blackhouse.Payments.Presenter;

namespace blackhouse.Payments {
    public class PaymentTransaction : IPaymentTransaction {

        #region Fields

        private IPaymentsDataAccess dataAccess;

        private int statusId;
        private PaymentTransactionStatus status;

        private string typeKey;
        private PaymentType type;

        #endregion

        #region Properties

        public int PaymentTrancactionId { get; private set; }
        public PayUConfig Configuration { get; private set; }

        public PaymentType PaymentType {
            get {
                if (this.type == null && !string.IsNullOrEmpty(this.typeKey))
                    this.type = new PaymentType(this.typeKey);
                return this.type;
            }
            set {
                this.type = value;
                this.typeKey = (this.type != null) ? this.type.PaymentKey : "";
            }
        }

        public string UserSessionId { get; private set; }
        public double ChargeValue { get; set; }
        public string Description { get; private set; }
        public int OrderId { get; set; }
        public string DescriptionExtension { get; set; }
        public string BankTransferDescription { get; set; }
        public string FirstName { get; private set; }
        public string LastName { get; private set; }
        public string Street { get; set; }
        public string StreetNumber { get; set; }
        public string FlatNumber { get; set; }
        public string City { get; set; }
        public string PostCode { get; set; }
        public string Country { get; set; }
        public string EMail { get; private set; }
        public string Phone { get; set; }
        public string ClientIP { get; private set; }
        public bool JsEnable { get; set; }
        public string PaybackLogin { get; set; }
        public string Sig { get; private set; }
        public string Ts { get; private set; }
        public PaymentTransactionStatus Status {
            get {
                if (this.status == null && this.statusId > 0)
                    this.status = new PaymentTransactionStatus(this.statusId);
                return this.status;
            }
            set {
                this.status = value;
                this.statusId = (this.status != null) ? this.status.StatusId : 0;
            }
        }

        private string language;
        public string Language {
            get { return language; }
            set {
                if (language != value && (value == "pl" || value == "en"))
                    language = value;
            }
        }
        public bool Exist { get; set; }
        public DateTime CreatedDate { get; set; }
        public DateTime BeginDate { get; set; }
        public DateTime SentDate { get; set; }
        public DateTime ReceiveDate { get; set; }
        public DateTime CancelDate { get; set; }

        #endregion

        #region Constructors

        public PaymentTransaction(int paymentTransactionId) {
            this.dataAccess = PaymentsDataFactory.Instance().GetPaymentDataAccess();
            this.Configuration = PayUConfig.Get();

            PaymentTransactionData data = this.dataAccess.GetPaymentTransaction(paymentTransactionId);
            if (data.TransactionId != paymentTransactionId)
                throw new Exception("Transakcja " + paymentTransactionId + " nie istnieje!~");
            this.SetCurrent(data);
        }

        private PaymentTransaction(PaymentTransactionData data) {
            this.Configuration = PayUConfig.Get();
            this.dataAccess = PaymentsDataFactory.Instance().GetPaymentDataAccess();
            if (this.Configuration.PosID != data.PosId)
                throw new Exception("Transakcja należy do POS'a który nie jest obsługiwany przez ten system. Sprawdź PayUConfig");

            this.SetCurrent(data);
        }

        public PaymentTransaction(string sessionId,
            double chargeValue,
            string description,
            string firstName,
            string lastName,
            string email,
            string clientIp) {

            this.dataAccess = PaymentsDataFactory.Instance().GetPaymentDataAccess();

            this.Configuration = PayUConfig.Get();
            this.UserSessionId = sessionId;
            this.ChargeValue = chargeValue;
            this.Description = description;
            this.FirstName = firstName;
            this.LastName = lastName;
            this.EMail = email;
            this.ClientIP = clientIp;

            this.SetDefaultPropertyValues();
        }

        #endregion

        #region Methods

        private void SetDefaultPropertyValues() {
            if (this.Configuration.Testing)
                this.PaymentType = Payments.PaymentType.Test;
            else
                this.PaymentType = Payments.PaymentType.NoDefined;
            this.Status = PaymentTransactionStatus.NewTransaction;
            this.JsEnable = true;
        }

        public void SaveNew() {
            this.Sign();
            PaymentTransactionData data = this.GetOfCurrent();
            this.PaymentTrancactionId = this.dataAccess.SaveNewPaymentTransaction(data);
            data = this.dataAccess.GetPaymentTransaction(this.PaymentTrancactionId);
            this.SetCurrent(data);
            this.Exist = true;
            this.AddLog();
        }

        protected void SetCurrent(PaymentTransactionData data) {

            this.PaymentTrancactionId = data.TransactionId;
            this.typeKey = data.PaymentType;
            this.statusId = data.Status;
            this.UserSessionId = data.UserSessionId;
            this.ChargeValue = data.ChargeValue;
            this.Description = data.Description;
            this.OrderId = data.OrderID;
            this.DescriptionExtension = data.Description2;
            this.BankTransferDescription = data.DescriptionBank;
            this.FirstName = data.FirstName;
            this.LastName = data.LastName;
            this.Street = data.Street;
            this.StreetNumber = data.StreetNumber;
            this.FlatNumber = data.FlatNumber;
            this.City = data.City;
            this.PostCode = data.PostCode;
            this.Country = data.Country;
            this.EMail = data.EMail;
            this.Phone = data.Phone;
            this.ClientIP = data.ClientIP;
            this.JsEnable = data.JavaScript;
            this.PaybackLogin = data.PaybackLogin;
            this.Sig = data.Sig;
            this.Ts = data.Ts;
            this.Language = data.Language;
            this.CreatedDate = data.CreatedDate;
            this.BeginDate = data.BeginDate;
            this.SentDate = data.SentDate;
            this.ReceiveDate = data.ReceiveDate;
            this.CancelDate = data.CancelDate;
            this.Exist = true;
        }

        protected PaymentTransactionData GetOfCurrent() {
            PaymentTransactionData data = new PaymentTransactionData();
            data.ChargeValue = this.ChargeValue;
            data.City = this.City;
            data.ClientIP = this.ClientIP;
            data.Country = this.Country;
            data.Description = this.Description;
            data.Description2 = this.DescriptionExtension;
            data.DescriptionBank = this.BankTransferDescription;
            data.EMail = this.EMail;
            data.FirstName = this.FirstName;
            data.UserSessionId = this.UserSessionId;
            data.FlatNumber = this.FlatNumber;
            data.JavaScript = this.JsEnable;
            data.Language = this.Language;
            data.LastName = this.LastName;
            data.OrderID = this.OrderId;
            data.PaybackLogin = this.PaybackLogin;
            data.PaymentType = this.PaymentType.PaymentKey;
            data.Phone = this.Phone;
            data.PosAuthKey = this.Configuration.PosAuthKey;
            data.PosId = this.Configuration.PosID;
            data.PostCode = this.PostCode;
            data.Sig = this.Sig;
            data.Status = this.Status.StatusId;
            data.Street = this.Street;
            data.StreetNumber = this.StreetNumber;
            data.Ts = this.Ts;
            data.CreatedDate = this.CreatedDate;
            data.BeginDate = this.BeginDate;
            data.SentDate = this.SentDate;
            data.ReceiveDate = this.ReceiveDate;
            data.CancelDate = this.CancelDate;
            return data;
        }

        private void Sign() {
            this.Ts = Convert.ToInt64(DateTime.UtcNow.TimeOfDay.TotalSeconds).ToString();
            this.Sig = this.Configuration.PosID
                + (this.PaymentType == Payments.PaymentType.NoDefined ? "" : this.PaymentType.PaymentKey)
                + this.UserSessionId
                + this.Configuration.PosAuthKey
                + (this.ChargeValue * 100)
                + this.Description
                + this.DescriptionExtension
                + this.BankTransferDescription
                + (this.OrderId > 0 ? this.OrderId.ToString() : "")
                + this.FirstName
                + this.LastName
                + this.PaybackLogin
                + this.Street 
                + this.StreetNumber
                + this.FlatNumber
                + this.City
                + this.PostCode
                + this.Country
                + this.EMail
                + this.Phone
                + this.Language
                + this.ClientIP
                + this.Ts
                + this.Configuration.KeyOut;
            this.Sig = this.Sig.GetMd5().ToLower().ReplaceEx("-", "");
        }

        public static PaymentTransaction Find(int posId, string transactionSession) {
            IPaymentsDataAccess dataAccess = PaymentsDataFactory.Instance().GetPaymentDataAccess();
            PaymentTransactionData data = dataAccess.FindPaymentTransaction(posId, transactionSession);
            if (data != null && data.PosId == posId)
                return new PaymentTransaction(data);
            else
                return null;
        }

        public static PaymentTransaction GetByOrderId(int orderId) {
            IPaymentsDataAccess dataAccess = PaymentsDataFactory.Instance().GetPaymentDataAccess();
            PaymentTransactionData data = dataAccess.GetPaymentTransactionByOrderId(orderId);
            if (data != null && data.OrderID == orderId)
                return new PaymentTransaction(data);
            else
                return null;
        }

        public void LogError(PaymentError paymentError) {
            this.dataAccess.AddPaymentTransactionErrorLog(this.PaymentTrancactionId, paymentError.ErrorCode);
        }

        public void LogPositive() {
            this.dataAccess.AddPaymentTransactionPositiveLog(this.PaymentTrancactionId);
        }

        public void AddLog() {
            this.dataAccess.AddPaymentTransactionLog(this.PaymentTrancactionId, this.GetOfCurrent());
        }

        public void Save() {
            this.dataAccess.SavePaymentTransaction(this.PaymentTrancactionId, this.GetOfCurrent());
            this.AddLog();
        }

        public void SaveUpdateSnapshot(string snapshot) {
            this.dataAccess.SavePaymentTransactionUpdateSnapshot(this.PaymentTrancactionId, snapshot);
        }

        #endregion

    }
}